﻿HmJS.$Import('core.fx');

/*
---
name: Louper
version: 1.1-1
description: Class to zoom image using real loupe
license: MIT-Style License (http://mifjs.net/license.txt)
download: http://mootools.net/forge/p/louper
source: http://github.com/creaven/Louper/

copyright: Anton Samoylov (http://mifjs.net)
authors: Anton Samoylov (http://mifjs.net)

requires: core:1.2.4:*
provides: Louper
...
*/

HmJS.register('image.effect.Louper', function ($ns) {

	return new Class({

		version: '1.1',

		Implements: [Options, Events],

		options: {
			radius: 40
		},

		initialize: function (element, options) {
			this.setOptions(options);
			var radius = this.options.radius;
			this.small = document.id(element);
			var src = this.options.big || this.small.get('big');
			if (Browser.ie) {
				if (!document.namespaces.v) {
					document.namespaces.add("v", "urn:schemas-microsoft-com:vml");
					document.createStyleSheet().cssText = "v\\:fill, v\\:oval{behavior:url(#default#VML);display:inline-block}";
				}
				var canvas = new Element('v:oval');
				canvas.stroked = false;
				canvas.style.width = radius * 2;
				canvas.style.height = radius * 2;
				var fill = new Element('v:fill').inject(canvas);
				fill.type = 'tile';
				fill.src = src;
				this.canvas = canvas;
				this.fill = fill;
			} else {
				this.canvas = new Element('canvas', { width: radius * 2, height: radius * 2 });
				this.context = this.canvas.getContext("2d");
			}
			this.canvas.setStyles({
				position: 'absolute',
				cursor: 'move'
			});
			this.big = new Element('img', { src: src }).setStyles({
				position: 'absolute',
				top: 0,
				left: 0
			});
			this.loupe = new Element('img', { src: this.options.loupe.src }).setStyles({
				cursor: 'move'
			});
			this.load([this.small, this.onSmallLoad], [this.big, this.onBigLoad], [this.loupe, this.onLoupeLoad]);
		},

		load: function () {
			var l = arguments.length;
			var count = l;
			for (var i = 0; i < l; i++) {
				var img = arguments[i][0];
				var fun = arguments[i][1];
				img.addEvent('load', function () {
					if (arguments.callee.loaded) { return; }
					arguments.callee.loaded = true;
					var fun = arguments[0];
					fun.call(this);
					--count;
					if (!count) { this.ready(); }
				} .bind(this, fun));
				if (img.complete) { img.fireEvent('load'); }
			}
		},

		onSmallLoad: function () {
			this.wrapper = new Element('div').wraps(this.small).setStyles({
				width: this.small.offsetWidth,
				height: this.small.offsetHeight,
				position: 'relative',
				overflow: 'visible'
			}).addEvent('mousedown', function (event) {
				event.preventDefault();
			});
			['margin', 'left', 'top', 'bottom', 'right', 'float', 'clear', 'border'].each(function (p) {
				var style = this.small.getStyle(p);
				var dflt = 'auto';
				if (['float', 'clear', 'border'].contains(p)) {
					dflt = 'none';
				}
				try {
					this.small.setStyle(p, dflt);
					this.wrapper.setStyle(p, style);
				} catch (e) { };
			}, this);
			this.smallSize = {
				width: this.small.width,
				height: this.small.height
			};
		},

		onBigLoad: function () {
			this.bigSize = {
				width: this.big.width,
				height: this.big.height
			};
		},

		onLoupeLoad: function () {
			var k = this.options.radius / this.options.loupe.radius;
			var width = this.loupe.width * k;
			var height = this.loupe.height * k;
			this.loupeSize = {
				width: width,
				height: height
			};
			this.loupe.setStyles({
				width: width,
				height: height,
				position: 'relative',
				zIndex: 2
			}).inject(this.wrapper);
			this.loupeWrapper = new Element('div').setStyles({
				position: 'absolute',
				top: 0,
				left: 0
			}).adopt(this.loupe);
			this.canvas.setStyles({
				position: 'absolute',
				left: this.options.loupe.x * k - this.options.radius,
				top: this.options.loupe.y * k - this.options.radius
			});
			if (Browser.ie) {
				var src = this.loupe.src;
				this.loupe = new Element('div').replaces(this.loupe).setStyles({
					width: width,
					height: height,
					position: 'relative',
					zIndex: 1
				});
				this.loupe.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "',sizingMethod='scale')";
				this.loupe.style.background = 'none';
			}
		},

		ready: function () {
			this.loupeWrapper.inject(this.wrapper);
			this.canvas.inject(this.loupeWrapper);
			this.loupeWrapper.setStyle('opacity', 0);
			this.wrapper.addEvents({
				mouseenter: this.showLoupe.bind(this),
				mouseleave: this.hideLoupe.bind(this)
			});
			var delta = {
				x: this.canvas.getStyle('left').toInt(),
				y: this.canvas.getStyle('top').toInt()
			};
			var extra = this.options.radius * (1 - this.smallSize.width / this.bigSize.width / Math.sqrt(2));
			this.loupeWrapper.makeDraggable({
				limit: {
					x: [Math.round(0 - delta.x - extra), Math.round(this.smallSize.width - delta.x + extra - 2 * this.options.radius)],
					y: [Math.round(0 - delta.y - extra), Math.round(this.smallSize.height - delta.y + extra - 2 * this.options.radius)]
				},
				preventDefault: true,
				onDrag: this.zoom.bind(this)
			});
			this.fireEvent('ready');
		},

		showLoupe: function () {
			this.position = this.small.getPosition();
			this.zoom();
			this.loupeWrapper.fade('in');
		},

		hideLoupe: function () {
			this.loupeWrapper.fade('out');
		},

		zoom: function () {
			var _canvas = this.canvas,
					radius = this.options.radius,
					loupeSize = radius * 2,
					pos = _canvas.getPosition(),
					_px = this.position.x,
					_py = this.position.y,
					_sw = this.smallSize.width,
					_sh = this.smallSize.height,
					_bw = this.bigSize.width,
					x = (pos.x - _px + loupeSize / 2) * _bw / _sw - loupeSize / 2,
					y = (pos.y - _py + loupeSize / 2) * this.bigSize.height / _sh - loupeSize / 2;
			if (!Browser.ie) {
				var context = this.context;
				try {
					context.save();
					context.beginPath();
					context.arc(radius, radius, radius, 0, Math.PI * 2, true);
					context.closePath();
					context.clip();
					context.fillStyle = 'rgb(255,255,255)';
					context.fillRect(0, 0, _canvas.width, _canvas.height);
					context.clearRect(0, 0, _canvas.width, _canvas.height);
					context.drawImage(this.big, -x, -y);
					context.restore();
				} catch (e) { };
			} else {
				this.fill.dispose();
				this.fill.position = -x / loupeSize + "," + -y / loupeSize;
				this.fill.inject(_canvas);
			};
			var extra = radius * (1 - _sw / _bw) / Math.sqrt(2);
			var limit = {
				left: _px - extra,
				right: _px + _sw + extra,
				top: _py - extra,
				bottom: _py + _sh + extra
			};
			var coords = _canvas.getCoordinates();
			var clip = {};
			['left', 'right', 'top', 'bottom'].each(function (side) {
				if (side == 'left' || side == 'top') {
					coords[side] = -coords[side];
					limit[side] = -limit[side];
				};
				clip[side] = coords[side] > limit[side] ? Math.ceil(coords[side] - limit[side]) : 'auto';
			});
			_canvas.setStyle('clip', 'rect(' +
			(clip.top != 'auto' ? clip.top + 'px' : 'auto') + ' ' +
			(clip.right != 'auto' ? (loupeSize - clip.right) + 'px' : 'auto') + ' ' +
			(clip.bottom != 'auto' ? (loupeSize - clip.bottom) + 'px' : 'auto') + ' ' +
			(clip.left != 'auto' ? clip.left + 'px' : 'auto') + ')');
		}

	});

});